package com.vcc.boss.controller;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.asymmetric.AsymmetricAlgorithm;
import cn.hutool.crypto.asymmetric.Sign;
import cn.hutool.crypto.asymmetric.SignAlgorithm;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson2.JSONObject;
import com.vcc.boss.core.queue.transaction.queue.RiskQueue;
import com.vcc.channel.base.model.request.TransferDto;
import com.vcc.channel.base.service.ChannelBaseService;
import com.vcc.channel.config.ChannelConfig;
import com.vcc.channel.service.service.LlService;
import com.vcc.core.constant.CardStatusEnum;
import com.vcc.core.constant.CardTypeEnum;
import com.vcc.core.constant.TransactionTypeConstant;
import com.vcc.core.exception.ProgramException;
import com.vcc.core.util.IdWorker;
import com.vcc.core.util.TransactionUtil;
import com.vcc.data.entity.*;
import com.vcc.service.service.*;
import jakarta.annotation.Resource;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.web.bind.annotation.*;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.security.PublicKey;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@RestController
@RequestMapping("message")
public class MessageController {

    private static final Logger logger = LoggerFactory.getLogger(MessageController.class);

    private static final List<String> transactionTypeList = List.of(
            "auth", "corrective_auth", "verification", "void", "refund", "corrective_refund", "corrective_refund_void", "recharge", "recharge_return", "discard_recharge_return", "service_fee", "refund_reversal", "fund_in", "atm_inquiry", "atm_withdrawals"
    );

    private static final List<String> settlementTypeList = List.of(
            "auth", "verification", "refund"
    );


    @Resource
    private WebhookSourceMessageService webhookSourceMessageService;
    @Resource
    private TransactionService transactionService;
    @Resource
    private UserCardService userCardService;
    @Resource
    private CardholderService cardholderService;
    @Resource
    private WalletDetailService walletDetailService;
    @Resource
    private CardTypeService cardTypeService;
    @Resource
    private UserCardWalletService userCardWalletService;
    @Resource
    private WalletService walletService;
    @Resource
    private RedissonClient redissonClient;
    @Resource
    private ChannelConfig channelConfig;
    @Resource
    private OtpService otpService;
    @Resource
    private LlService llService;
    @Resource
    private RiskQueue riskQueue;
    @Autowired
    private ShareAccountDetailService shareAccountDetailService;
    @Autowired
    private ApplicationContext applicationContext;
    @Autowired
    private ShareAccountService shareAccountService;
    @Autowired
    private ChannelService channelService;
    @Autowired
    private ShareAccountConfigService shareAccountConfigService;

    private String photoCategoryConvert(String category, String type) {
        if (category.equals("issuing")) {
            if (transactionTypeList.contains(type)) {
                return "transaction";
            } else {
                return "cardholder";
            }
        } else if (category.equals("issuing_settlement")) {
            return "settle";
        } else {
            return "card";
        }
    }

    private boolean checkMessage(String requestBody, String signValue) {
        PublicKey publicKey =
                SecureUtil.generatePublicKey(AsymmetricAlgorithm.RSA.getValue(), Base64.decode(channelConfig.getPhoton().getChannelPublicKey()));
        Sign sign = SecureUtil.sign(SignAlgorithm.MD5withRSA);
        sign.setPublicKey(publicKey);
        Base64.decode(signValue);
        return sign.verify(requestBody.getBytes(), Base64.decode(signValue));
    }

    @PostMapping("photonPayMessage")
    public HashMap<String, Object> photonPayMessage(
            @RequestHeader("X-PD-NOTIFICATION-CATAGORY") String category,
            @RequestHeader("X-PD-NOTIFICATION-TYPE") String type,
            @RequestHeader("X-PD-SIGN") String sign,
            @RequestBody String body
    ) {
        logger.info("Received PhotonPay message: category={}, type={}, sign={}, body={}", category, type, sign, body);
        JSONObject requestBody;
        try {
            requestBody = JSONUtil.toBean(body, JSONObject.class);
        } catch (Exception e) {
            logger.error("Error parsing request body: {}", e.getMessage(), e);
            return MapUtil.of("roger", false);
        }
        String transactionId = requestBody.getString("transactionId");
        String transactionStatus = category.equals("issuing_settlement") ? requestBody.getString("transactionStatus") : requestBody.getString("status");
//        if (!checkMessage(body, sign)) {
//            HashMap<String, Object> map = HttpUtil.createBody();
//            map.put("code", 403);
//            map.put("msg", "sign error");
//            return map;
//        }
        RLock lock = redissonClient.getLock("photonPayMessage:" + transactionId);
        try {
            lock.lock();
            if (transactionId == null) {
                transactionId = category + "-" + type + "-" + System.currentTimeMillis();
            }
            List<WebhookSourceMessage> webhookSourceMessage = webhookSourceMessageService.getByTransactionId(transactionId);
            //判断订单状态是否在我们存储的订单内是否有相同状态
            // 如果存在相同的 transactionId 的消息，则不再保存，直接返回
            // 这里可以使用 Java 8 的 Stream API 来简化判断逻辑
            String finalTransactionId = transactionId;
            if (!webhookSourceMessage.isEmpty()
                    &&
                    webhookSourceMessage.stream()
                            .anyMatch(msg ->
                                    msg.getTransactionId().equals(finalTransactionId) &&
                                            msg.getWebhookEvent().equals(photoCategoryConvert(category, type)) &&
                                            msg.getTransactionStatus().equals(transactionStatus))) {
                logger.warn("Webhook message with transactionId {} already exists, skipping save.", transactionId);
                return MapUtil.of("roger", true);
            }
            webhookSourceMessageService.save(
                    WebhookSourceMessage.builder()
                            .transactionId(transactionId)
                            .sourceInfo(body)
                            .hasProcessed(false)
                            .transactionStatus(transactionStatus)
                            .channel("PhotonPay")
                            .webhookEvent(photoCategoryConvert(category, type))
                            .type(type)
                            .build()
            );
            String cardId = requestBody.getString("cardId");
            UserCard userCard = userCardService.getByChannelCardIdOrNullAndEmpty(cardId);
            ChannelBaseService channelBaseService = applicationContext.getBean(userCard.getChannelCode(), ChannelBaseService.class);
            if (userCard == null) {
                logger.warn("Card not found for cardId: {}", cardId);
                return MapUtil.of("roger", true);
            }
            switch (category) {
                case "issuing" -> {
                    if (transactionTypeList.contains(type)) {
                        //交易
                        logger.info("PhotonPay transaction message: {}", body);
                        String originTransactionId = requestBody.getString("originTransactionId");
                        String transactionType = requestBody.getString("transactionType");
                        String status = requestBody.getString("status");
                        String code = requestBody.getString("code");
                        String msg = requestBody.getString("msg");
                        String mcc = requestBody.getString("mcc");
                        BigDecimal transactionAmount = requestBody.getBigDecimal("transactionAmount").abs();
                        String transactionCurrency = requestBody.getString("transactionCurrency");
                        BigDecimal txnPrincipalChangeAmount = requestBody.getBigDecimal("txnPrincipalChangeAmount").abs();
                        String txnPrincipalChangeCurrency = requestBody.getString("txnPrincipalChangeCurrency");
                        BigDecimal feeDeductionAmount = requestBody.getBigDecimal("feeDeductionAmount").abs();
                        String feeDeductionCurrency = requestBody.getString("feeDeductionCurrency");
                        String feeDetailJson = requestBody.getString("feeDetailJson");
                        String merchantNameLocation = requestBody.getString("merchantNameLocation");
                        String merchantLocation = requestBody.getString("merchantLocation");
                        BigDecimal balance = requestBody.getBigDecimal("availableTransactionLimit");
                        userCardService.updateUsableQuotaByChannelCardId(
                                balance != null ? balance : BigDecimal.ZERO,
                                userCard.getChannelCardId()
                        );
                        DateTime transactionTime = DateUtil.parse(requestBody.getString("createdAt"), DatePattern.UTC_SIMPLE_PATTERN).offset(DateField.HOUR_OF_DAY, 8);
                        if (
                                TransactionTypeConstant.AUTH_LIST.contains(transactionType) ||
                                        TransactionTypeConstant.VERIFY_LIST.contains(transactionType) ||
                                        TransactionTypeConstant.REFUND_LIST.contains(transactionType) ||
                                        TransactionTypeConstant.REVERSAL_LIST.contains(transactionType)
                        ) {
                            if (TransactionTypeConstant.AUTH_LIST.contains(transactionType)) {
                                if (transactionService.existsByChannelTranId(transactionId)) {
                                    logger.warn("Transaction with ID {} already exists, skipping save.", transactionId);
                                    transactionService.updateStatusByChannelTranId(
                                            transactionId,
                                            TransactionUtil.statusHandle(status)
                                    );
                                    return MapUtil.of("roger", true);
                                }
                                Transaction transaction = Transaction.builder()
                                        .tranId(IdUtil.getSnowflakeNextIdStr())
                                        .channelCardId(cardId)
                                        .channelTranId(transactionId)
                                        .originalTranId("")
                                        .originalChannelTranId("")
                                        .channel("PhotonPay")
                                        .cardNumber(userCard.getCardNumber())
                                        .userCardId(userCard.getId())
                                        .userId(userCard.getUserId())
                                        .cardTypeId(userCard.getCardTypeId())
                                        .merchantName(merchantNameLocation)
                                        .tranDate(transactionTime)
                                        .merchantAmount(transactionAmount)
                                        .merchantCurrency(transactionCurrency)
                                        .billingAmount(txnPrincipalChangeAmount)
                                        .billingCurrency(txnPrincipalChangeCurrency)
                                        .settleAmount(BigDecimal.ZERO)
                                        .settleCurrency("")
                                        .settleDate(null)
                                        .postDate(null)
                                        .transactionType("AUTH")
                                        .status(TransactionUtil.statusHandle(status))
                                        .code(code)
                                        .remark(msg)
                                        .mcc(mcc)
                                        .fee(feeDeductionAmount)
                                        .feeCurrency(feeDeductionCurrency)
                                        .feeInfo(feeDetailJson)
                                        .merchantCountry(merchantLocation)
                                        .merchantCity("")
                                        .cardType(userCard.getCardType())
                                        .shareAccountId(userCard.getShareAccountId())
                                        .build();
                                transactionService.save(transaction);
                                ShareAccount shareAccount = shareAccountService.getById(userCard.getShareAccountId());
                                if (userCard.getCardType().equals(CardTypeEnum.SHARE.getCode()) && shareAccount != null) {
                                    shareAccountDetailService.create(
                                            shareAccount.getId(),
                                            channelBaseService.queryAccountBalance(shareAccount.getAccountId()),
                                            "USD",
                                            shareAccount.getChannelCode(),
                                            "AUTH",
                                            transaction.getTranId(),
                                            transaction.getBillingAmount(),
                                            "",
                                            false,
                                            userCard.getCardTypeId(),
                                            userCard.getId(),
                                            shareAccount.getUserId(),
                                            userCard.getUserId()
                                    );
                                }
                                riskQueue.add(transactionId);
                                // 处理钱包明细
                                auth(
                                        transactionId,
                                        transaction.getTranId(),
                                        userCard,
                                        cardTypeService.getById(userCard.getCardTypeId()),
                                        userCard.getCardType().equals(CardTypeEnum.SHARE.getCode()) ? shareAccountService.getById(userCard.getShareAccountId()) : null,
                                        userCard.getCardType().equals(CardTypeEnum.NORMAL.getCode()) ? userCardWalletService.findByUserIdAndCardId(userCard.getUserId(), userCard.getCardTypeId()) : null,
                                        userCard.getCardType().equals(CardTypeEnum.SHARE.getCode()) ? shareAccountConfigService.getByUserIdAndCardTypeId(userCard.getUserId(), userCard.getCardTypeId()) : null,
                                        channelBaseService,
                                        txnPrincipalChangeAmount.abs(),
                                        TransactionUtil.statusHandle(status),
                                        merchantLocation
                                );
                            }
                            if (TransactionTypeConstant.VERIFY_LIST.contains(transactionType)) {
                                if (transactionService.existsByChannelTranId(transactionId)) {
                                    logger.warn("Transaction with ID {} already exists, skipping save.", transactionId);
                                    transactionService.updateStatusByChannelTranId(
                                            transactionId,
                                            TransactionUtil.statusHandle(status)
                                    );
                                    return MapUtil.of("roger", true);
                                }
                                Transaction transaction = Transaction.builder()
                                        .tranId(IdUtil.getSnowflakeNextIdStr())
                                        .channelCardId(cardId)
                                        .channelTranId(transactionId)
                                        .originalTranId("")
                                        .originalChannelTranId("")
                                        .channel("PhotonPay")
                                        .cardNumber(userCard.getCardNumber())
                                        .userCardId(userCard.getId())
                                        .userId(userCard.getUserId())
                                        .cardTypeId(userCard.getCardTypeId())
                                        .merchantName(merchantNameLocation)
                                        .tranDate(transactionTime)
                                        .merchantAmount(transactionAmount)
                                        .merchantCurrency(transactionCurrency)
                                        .billingAmount(txnPrincipalChangeAmount)
                                        .billingCurrency(txnPrincipalChangeCurrency)
                                        .settleAmount(BigDecimal.ZERO)
                                        .settleCurrency("")
                                        .settleDate(null)
                                        .postDate(null)
                                        .hasClear(false)
                                        .transactionType("VERIFY")
                                        .status(TransactionUtil.statusHandle(status))
                                        .code(code)
                                        .remark(msg)
                                        .mcc(mcc)
                                        .fee(feeDeductionAmount)
                                        .feeCurrency(feeDeductionCurrency)
                                        .feeInfo(feeDetailJson)
                                        .merchantCountry(merchantLocation)
                                        .merchantCity("")
                                        .cardType(userCard.getCardType())
                                        .shareAccountId(userCard.getShareAccountId())
                                        .build();
                                transactionService.save(transaction);
                                riskQueue.add(transactionId);
                            }
                            if (TransactionTypeConstant.REFUND_LIST.contains(transactionType)) {
                                String originTranId = "";
                                if (StringUtils.isNotBlank(originTransactionId) && transactionService.existsByChannelTranId(originTransactionId)) {
                                    Transaction transaction = transactionService.getByChannelTranId(originTransactionId);
                                    originTranId = transaction.getTranId();
                                    transactionService.updateStatusById(transaction.getId(), "SUCCESS");
                                }
                                DateTime completedTime = DateUtil.parse(requestBody.getString("updatedAt"), DatePattern.UTC_SIMPLE_PATTERN).offset(DateField.HOUR_OF_DAY, 8);
                                String transId = IdUtil.getSnowflakeNextIdStr();
                                if (transactionService.existsByChannelTranId(transactionId)) {
                                    if (!TransactionUtil.statusHandle(status).equals("SUCCESS")) {
                                        logger.warn("Transaction with ID {} already exists but is not successful, skipping save.", transactionId);
                                        return MapUtil.of("roger", true);
                                    }
                                    logger.warn("Transaction with ID {} already exists, skipping save.", transactionId);
                                    transactionService.updateRefundTran(
                                            transactionId,
                                            TransactionUtil.statusHandle(status),
                                            txnPrincipalChangeAmount,
                                            txnPrincipalChangeCurrency,
                                            completedTime,
                                            completedTime,
                                            true
                                    );
                                    transId = transactionService.getTranIdByChannelTranId(transactionId);
                                } else {
                                    Transaction transaction = Transaction.builder()
                                            .tranId(transId)
                                            .channelCardId(cardId)
                                            .channelTranId(transactionId)
                                            .originalTranId(originTranId)
                                            .originalChannelTranId(originTransactionId == null ? "" : originTransactionId)
                                            .channel("PhotonPay")
                                            .cardNumber(userCard.getCardNumber())
                                            .userCardId(userCard.getId())
                                            .userId(userCard.getUserId())
                                            .cardTypeId(userCard.getCardTypeId())
                                            .merchantName(merchantNameLocation)
                                            .tranDate(transactionTime)
                                            .merchantAmount(transactionAmount)
                                            .merchantCurrency(transactionCurrency)
                                            .billingAmount(txnPrincipalChangeAmount)
                                            .billingCurrency(txnPrincipalChangeCurrency)
                                            .settleAmount(BigDecimal.ZERO)
                                            .settleCurrency("")
                                            .settleDate(null)
                                            .postDate(null)
                                            .hasClear(false)
                                            .transactionType("REFUND")
                                            .status(TransactionUtil.statusHandle(status))
                                            .code(code)
                                            .remark(msg)
                                            .mcc(mcc)
                                            .fee(feeDeductionAmount)
                                            .feeCurrency(feeDeductionCurrency)
                                            .feeInfo(feeDetailJson)
                                            .merchantCountry(merchantLocation)
                                            .cardType(userCard.getCardType())
                                            .merchantCity("")
                                            .shareAccountId(userCard.getShareAccountId())
                                            .build();
                                    transactionService.save(transaction);
                                    ShareAccount shareAccount = shareAccountService.getById(userCard.getShareAccountId());
                                    if (userCard.getCardType().equals(CardTypeEnum.SHARE.getCode()) && shareAccount != null) {
                                        shareAccountDetailService.create(
                                                shareAccount.getId(),
                                                channelBaseService.queryAccountBalance(shareAccount.getAccountId()),
                                                "USD",
                                                shareAccount.getChannelCode(),
                                                "REFUND",
                                                transaction.getTranId(),
                                                transaction.getBillingAmount(),
                                                "",
                                                true,
                                                userCard.getCardTypeId(),
                                                userCard.getId(),
                                                shareAccount.getUserId(),
                                                userCard.getUserId()
                                        );
                                    }
                                }
                                // 处理钱包明细
                                if (TransactionUtil.statusHandle(status).equals("SUCCESS")) {
                                    refund(
                                            transactionId,
                                            transId,
                                            userCard,
                                            cardTypeService.getById(userCard.getCardTypeId()),
                                            userCard.getCardType().equals(CardTypeEnum.SHARE.getCode()) ? shareAccountService.getById(userCard.getShareAccountId()) : null,
                                            userCard.getCardType().equals(CardTypeEnum.SHARE.getCode()) ? shareAccountConfigService.getByUserIdAndCardTypeId(userCard.getUserId(), userCard.getCardTypeId()) : null,
                                            channelBaseService,
                                            userCard.getCardType().equals(CardTypeEnum.SHARE.getCode()) ? null : userCardWalletService.findByUserIdAndCardId(userCard.getUserId(), userCard.getCardTypeId()),
                                            txnPrincipalChangeAmount.abs()
                                    );
                                }
                            }
                            if (TransactionTypeConstant.REVERSAL_LIST.contains(transactionType)) {
                                String originTranId = "";
                                Transaction originTransaction;
                                if (StringUtils.isNotBlank(originTransactionId) && transactionService.existsByChannelTranId(originTransactionId)) {
                                    originTransaction = transactionService.getByChannelTranId(originTransactionId);
                                    originTranId = originTransaction.getTranId();
                                    transactionService.updateStatusById(originTransaction.getId(), "SUCCESS");
                                }
                                DateTime completedTime = DateUtil.parse(requestBody.getString("updatedAt"), DatePattern.UTC_SIMPLE_PATTERN).offset(DateField.HOUR_OF_DAY, 8);
                                String transId = IdUtil.getSnowflakeNextIdStr();
                                if (transactionService.existsByChannelTranId(transactionId)) {
                                    if (!TransactionUtil.statusHandle(status).equals("SUCCESS")) {
                                        logger.warn("Transaction with ID {} already exists but is not successful, skipping save.", transactionId);
                                        return MapUtil.of("roger", true);
                                    }
                                    logger.warn("Transaction with ID {} already exists, skipping save.", transactionId);
                                    transactionService.updateReversalTran(
                                            transactionId,
                                            TransactionUtil.statusHandle(status),
                                            txnPrincipalChangeAmount,
                                            txnPrincipalChangeCurrency,
                                            completedTime,
                                            completedTime,
                                            true
                                    );
                                    transId = transactionService.getTranIdByChannelTranId(transactionId);
                                } else {
                                    //不存在
                                    Transaction transaction = Transaction.builder()
                                            .tranId(transId)
                                            .channelCardId(cardId)
                                            .channelTranId(transactionId)
                                            .originalTranId(originTranId)
                                            .originalChannelTranId(originTransactionId == null ? "" : originTransactionId)
                                            .channel("PhotonPay")
                                            .cardNumber(userCard.getCardNumber())
                                            .userCardId(userCard.getId())
                                            .userId(userCard.getUserId())
                                            .cardTypeId(userCard.getCardTypeId())
                                            .merchantName(merchantNameLocation)
                                            .tranDate(transactionTime)
                                            .merchantAmount(transactionAmount)
                                            .merchantCurrency(transactionCurrency)
                                            .billingAmount(txnPrincipalChangeAmount)
                                            .billingCurrency(txnPrincipalChangeCurrency)
                                            .settleAmount(txnPrincipalChangeAmount)
                                            .settleCurrency(txnPrincipalChangeCurrency)
                                            .settleDate(completedTime)
                                            .postDate(completedTime)
                                            .hasClear(true)
                                            .transactionType("REVERSAL")
                                            .status(TransactionUtil.statusHandle(status))
                                            .code(code)
                                            .remark(msg)
                                            .mcc(mcc)
                                            .fee(feeDeductionAmount)
                                            .feeCurrency(feeDeductionCurrency)
                                            .feeInfo(feeDetailJson)
                                            .merchantCountry(merchantLocation)
                                            .cardType(userCard.getCardType())
                                            .merchantCity("")
                                            .shareAccountId(userCard.getShareAccountId())
                                            .build();
                                    transactionService.save(transaction);
                                    ShareAccount shareAccount = shareAccountService.getById(userCard.getShareAccountId());
                                    if (userCard.getCardType().equals(CardTypeEnum.SHARE.getCode()) && shareAccount != null) {
                                        shareAccountDetailService.create(
                                                shareAccount.getId(),
                                                channelBaseService.queryAccountBalance(shareAccount.getAccountId()),
                                                "USD",
                                                shareAccount.getChannelCode(),
                                                "REVERSAL",
                                                transaction.getTranId(),
                                                transaction.getBillingAmount(),
                                                "",
                                                true,
                                                userCard.getCardTypeId(),
                                                userCard.getId(),
                                                shareAccount.getUserId(),
                                                userCard.getUserId()
                                        );
                                    }
                                }
                                if (TransactionUtil.statusHandle(status).equals("SUCCESS")) {
                                    // 处理钱包明细
                                    reversal(
                                            transactionId,
                                            transId,
                                            userCard,
                                            cardTypeService.getById(userCard.getCardTypeId()),
                                            userCard.getCardType().equals(CardTypeEnum.SHARE.getCode()) ? shareAccountService.getById(userCard.getShareAccountId()) : null,
                                            userCard.getCardType().equals(CardTypeEnum.SHARE.getCode()) ? shareAccountConfigService.getByUserIdAndCardTypeId(userCard.getUserId(), userCard.getCardTypeId()) : null,
                                            channelBaseService,
                                            userCard.getCardType().equals(CardTypeEnum.SHARE.getCode()) ? null : userCardWalletService.findByUserIdAndCardId(userCard.getUserId(), userCard.getCardTypeId()),
                                            txnPrincipalChangeAmount.abs()
                                    );
                                }
                                return MapUtil.of("roger", true);
                            }

                        }
                    } else {
                        //卡持有人
                        logger.info("PhotonPay cardholder message: {}", body);
                    }
                }
                case "issuing_settlement" -> {
                    //结算
                    if (settlementTypeList.contains(type)) {
                        logger.info("PhotonPay settlement message: {}", body);
                        String originTransactionId = requestBody.getString("originTransactionId");
                        String transactionType = requestBody.getString("transactionType");
                        String status = requestBody.getString("transactionStatus");
                        String code = requestBody.getString("code");
                        BigDecimal settleAmount = requestBody.getBigDecimal("settleAmount").abs();
                        String settleCurrency = requestBody.getString("settleCurrency");
                        String tranId = transactionService.getTranIdByChannelTranId(transactionId);
                        transactionService.updateTranSettleData(
                                transactionId,
                                transactionId,
                                tranId,
                                TransactionUtil.statusHandle(status),
                                settleAmount,
                                settleCurrency,
                                new Date(),
                                new Date(),
                                true
                        );
                    }
                }
            }
        } catch (Exception e) {
            logger.error("Error processing PhotonPay message: {}", e.getMessage(), e);
            return MapUtil.of("roger", false);
        } finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
        return MapUtil.of("roger", true);
    }

    @PostMapping("llMessage")
    public void llMessage(
            @RequestHeader("LLPAY-Signature") String signature,
            @RequestBody String body
    ) {
        logger.info("Received LL message: signature={}, body={}", signature, body);
        JSONObject payload;
        try {
            payload = JSONUtil.toBean(body, JSONObject.class);
        } catch (Exception e) {
            logger.error("Error parsing request body: {}", e.getMessage(), e);
            return;
        }
        JSONObject requestBody = JSONUtil.toBean(payload.getString("data"), JSONObject.class);
        String cardId = requestBody.getString("cardId");
        UserCard userCard = userCardService.getByChannelCardIdOrNullAndEmpty(cardId);
        if (userCard == null) {
            logger.warn("Card not found for cardId: {}", cardId);
            return;
        }
        CardType cardType = cardTypeService.getById(userCard.getCardTypeId());
        UserCardWallet userCardWallet = userCardWalletService.findByUserIdAndCardId(userCard.getUserId(), userCard.getCardTypeId());
        switch (payload.getString("topic")) {
            //更新卡限额
            case "CARD_LIMIT_CHANGED" -> {
                logger.info("Processing CARD_LIMIT_CHANGED for cardId: {}", cardId);
                BigDecimal balance = llService.queryCardBalance(cardId);
                if (balance != null) {
                    userCardService.updateUsableQuotaByChannelCardId(
                            balance,
                            userCard.getChannelCardId()
                    );
                    logger.info("Updated usable quota for cardId: {}, new balance: {}", cardId, balance);
                } else {
                    logger.warn("Failed to retrieve balance for cardId: {}", cardId);
                }
            }
            //修改卡状态
            case "CARD_STATUS_CHANGED" -> {
                logger.info("Processing CARD_STATUS_CHANGED for cardId: {}", cardId);
                String status = llService.queryCardStatus(cardId);
                if (status != null) {
                    if (userCard.getCardStatus().equals(CardStatusEnum.CONTROL.getCode()) && status.equals(CardStatusEnum.BLOCKED.getCode())) {
                        //如果当前状态是管控，变更状态是冻结，则不更新状态
                        logger.info("Card status change from CONTROL to BLOCKED ignored for cardId: {}", cardId);
                        return;
                    }
                    userCardService.updateCardStatusById(status, userCard.getId());
                    logger.info("Updated card status for cardId: {}, new status: {}", cardId, status);
                } else {
                    logger.warn("Status is null for  cardId: {}", cardId);
                }
            }
            //卡3DS
            case "CARD_3DS_OTP" -> {
                Otp otp = Otp.builder()
                        .userId(userCard.getUserId())
                        .code(requestBody.getString("otp"))
                        .type("TRANSACTION")
                        .userCardId(userCard.getId())
                        .cardTypeId(userCard.getCardTypeId())
                        .cardholderId(userCard.getCardholderId())
                        .currency(requestBody.getString("transactionCurrency"))
                        .amount(requestBody.getBigDecimal("transactionAmount"))
                        .cardNumber(userCard.getCardNumber())
                        .lastFourCardNumber(userCard.getCardNumberLastFour())
                        .merchantName(requestBody.getString("merchantName"))
                        .sourceInfo(body)
                        .build();
                otpService.save(otp);
            }
            //授权
            case "AUTH_EVENT", "CARD_VERIFY_EVENT" -> {
                String transactionId = requestBody.getString("transactionId");
                String transactionStatus = requestBody.getString("status");
                String type = requestBody.getString("type");
                type = StringUtils.isBlank(type) ? "VERIFY" : TransactionUtil.typeHandle(type);
                RLock lock = redissonClient.getLock("llMessage:" + transactionId);
                try {
                    lock.lock();
                    if (transactionId == null) {
                        transactionId = payload.getString("topic") + "-" + System.currentTimeMillis();
                    }
                    List<WebhookSourceMessage> webhookSourceMessage = webhookSourceMessageService.getByTransactionId(transactionId);
                    //判断订单状态是否在我们存储的订单内是否有相同状态
                    // 如果存在相同的 transactionId 的消息，则不再保存，直接返回
                    // 这里可以使用 Java 8 的 Stream API 来简化判断逻辑
                    String finalTransactionId = transactionId;
                    if (!webhookSourceMessage.isEmpty()
                            &&
                            webhookSourceMessage.stream()
                                    .anyMatch(msg ->
                                            msg.getTransactionId().equals(finalTransactionId) &&
                                                    msg.getWebhookEvent().equals("transaction") &&
                                                    msg.getTransactionStatus().equals(transactionStatus))) {
                        logger.warn("Webhook message with transactionId {} already exists, skipping save.", transactionId);
                        return;
                    }
                    webhookSourceMessageService.save(
                            WebhookSourceMessage.builder()
                                    .transactionId(transactionId)
                                    .sourceInfo(body)
                                    .hasProcessed(false)
                                    .transactionStatus(transactionStatus)
                                    .channel("LL")
                                    .webhookEvent("transaction")
                                    .type(type)
                                    .build()
                    );
                    String transactionCurrency = requestBody.getString("transactionCurrency");
                    String cardName = requestBody.getString("cardName");
                    String last4CardNum = requestBody.getString("last4CardNum");
                    Date transactionTime = DateUtil.parse(
                            requestBody.getString("transactionTime"),
                            DatePattern.NORM_DATETIME_PATTERN
                    );
                    String mcc = requestBody.getString("mcc");
                    String merchantName = requestBody.getString("merchantName");
                    String errorMsg = requestBody.getString("errorMsg");
                    String merchantId = requestBody.getString("merchantId");
                    BigDecimal transactionAmount = requestBody.getBigDecimal("transactionAmount").abs();
                    String errorMsgCode = requestBody.getString("errorMsgCode");
                    String originalTransactionId = requestBody.getString("originalTransactionId");
                    String billingCurrency = StringUtils.isBlank(requestBody.getString("billingCurrency")) ?
                            "" :
                            requestBody.getString("billingCurrency");
                    BigDecimal billingAmount = !requestBody.containsKey("billingAmount") ?
                            BigDecimal.ZERO :
                            requestBody.getBigDecimal("billingAmount").abs();
                    String merchantCountryCode = requestBody.getString("merchantCountryCode");
                    BigDecimal balance = llService.queryCardBalance(cardId);
                    if (balance != null) {
                        userCardService.updateUsableQuotaByChannelCardId(
                                balance,
                                userCard.getChannelCardId()
                        );
                        logger.info("Updated usable quota for cardId: {}, new balance: {}", cardId, balance);
                    } else {
                        logger.warn("Failed to retrieve balance for cardId: {}", cardId);
                    }
                    switch (type) {
                        case "AUTH" -> {
                            if (transactionService.existsByChannelTranId(transactionId)) {
                                logger.warn("Transaction with ID {} already exists, skipping save.", transactionId);
                                transactionService.updateStatusByChannelTranId(
                                        transactionId,
                                        TransactionUtil.statusHandle(transactionStatus)
                                );
                                return;
                            }
                            Transaction transaction = Transaction.builder()
                                    .tranId(IdUtil.getSnowflakeNextIdStr())
                                    .channelCardId(cardId)
                                    .channelTranId(transactionId)
                                    .originalTranId("")
                                    .originalChannelTranId(originalTransactionId == null ? "" : originalTransactionId)
                                    .channel("LL")
                                    .userCardId(userCard.getId())
                                    .userId(userCard.getUserId())
                                    .cardTypeId(userCard.getCardTypeId())
                                    .cardNumber(userCard.getCardNumber())
                                    .merchantName(merchantName)
                                    .tranDate(transactionTime)
                                    .merchantAmount(transactionAmount)
                                    .merchantCurrency(transactionCurrency)
                                    .billingAmount(billingAmount)
                                    .billingCurrency(billingCurrency)
                                    .settleAmount(BigDecimal.ZERO)
                                    .settleCurrency("")
                                    .settleDate(null)
                                    .postDate(null)
                                    .transactionType("AUTH")
                                    .status(TransactionUtil.statusHandle(transactionStatus))
                                    .code(errorMsgCode)
                                    .remark(errorMsg)
                                    .mcc(mcc)
                                    .fee(BigDecimal.ZERO) // LL does not provide fee information in AUTH
                                    .feeCurrency("")
                                    .feeInfo("")
                                    .merchantCountry(merchantCountryCode)
                                    .merchantCity("")
                                    .cardType(userCard.getCardType())
                                    .shareAccountId(userCard.getShareAccountId())
                                    .build();
                            transactionService.save(transaction);
                            riskQueue.add(transactionId);
                            // 处理钱包明细
                            auth(
                                    transactionId,
                                    transaction.getTranId(),
                                    userCard,
                                    cardType,
                                    null,
                                    userCardWallet,
                                    null,
                                    null,
                                    billingAmount.abs(),
                                    TransactionUtil.statusHandle(transactionStatus),
                                    merchantCountryCode
                            );
                        }
                        case "REVERSAL" -> {
                            String originTranId = "";
                            Transaction originTransaction;
                            if (StringUtils.isNotBlank(originalTransactionId) && transactionService.existsByChannelTranId(originalTransactionId)) {
                                originTransaction = transactionService.getByChannelTranId(originalTransactionId);
                                originTranId = originTransaction.getTranId();
                                transactionService.updateStatusById(originTransaction.getId(), "SUCCESS");
                            }
                            String transId = IdUtil.getSnowflakeNextIdStr();
                            if (transactionService.existsByChannelTranId(transactionId)) {
                                if (!TransactionUtil.statusHandle(transactionStatus).equals("SUCCESS")) {
                                    logger.warn("Transaction with ID {} already exists but is not successful, skipping save.", transactionId);
                                    return;
                                }
                                logger.warn("Transaction with ID {} already exists, skipping save.", transactionId);
                                transactionService.updateReversalTran(
                                        transactionId,
                                        TransactionUtil.statusHandle(transactionStatus),
                                        billingAmount,
                                        billingCurrency,
                                        transactionTime,
                                        transactionTime,
                                        true
                                );
                                transId = transactionService.getTranIdByChannelTranId(transactionId);
                            } else {
                                //不存在
                                Transaction transaction = Transaction.builder()
                                        .tranId(transId)
                                        .channelCardId(cardId)
                                        .channelTranId(transactionId)
                                        .originalTranId(originTranId)
                                        .originalChannelTranId(originalTransactionId == null ? "" : originalTransactionId)
                                        .channel("LL")
                                        .cardNumber(userCard.getCardNumber())
                                        .userCardId(userCard.getId())
                                        .userId(userCard.getUserId())
                                        .cardTypeId(userCard.getCardTypeId())
                                        .merchantName(merchantName)
                                        .tranDate(transactionTime)
                                        .merchantAmount(transactionAmount)
                                        .merchantCurrency(transactionCurrency)
                                        .billingAmount(billingAmount)
                                        .billingCurrency(billingCurrency)
                                        .settleAmount(BigDecimal.ZERO)
                                        .settleCurrency("")
                                        .settleDate(transactionTime)
                                        .postDate(transactionTime)
                                        .hasClear(true)
                                        .transactionType("REVERSAL")
                                        .status(TransactionUtil.statusHandle(transactionStatus))
                                        .code(errorMsgCode)
                                        .remark(errorMsg)
                                        .mcc(mcc)
                                        .fee(BigDecimal.ZERO) // LL does not provide fee information in AUTH
                                        .feeCurrency("")
                                        .feeInfo("")
                                        .merchantCountry(merchantCountryCode)
                                        .cardType(userCard.getCardType())
                                        .merchantCity("")
                                        .shareAccountId(userCard.getShareAccountId())
                                        .build();
                                transactionService.save(transaction);

                            }
                            if (TransactionUtil.statusHandle(transactionStatus).equals("SUCCESS")) {
                                // 处理钱包明细
                                reversal(
                                        transactionId,
                                        transId,
                                        userCard,
                                        cardTypeService.getById(userCard.getCardTypeId()),
                                        null,
                                        null,
                                        null,
                                        userCardWalletService.findByUserIdAndCardId(userCard.getUserId(), userCard.getCardTypeId()),
                                        billingAmount.abs()
                                );
                            }
                        }
                        case "VERIFY" -> {
                            Transaction transaction = Transaction.builder()
                                    .tranId(IdUtil.getSnowflakeNextIdStr())
                                    .channelCardId(cardId)
                                    .channelTranId(transactionId)
                                    .originalTranId("")
                                    .originalChannelTranId(originalTransactionId == null ? "" : originalTransactionId)
                                    .channel("LL")
                                    .userCardId(userCard.getId())
                                    .userId(userCard.getUserId())
                                    .cardTypeId(userCard.getCardTypeId())
                                    .cardNumber(userCard.getCardNumber())
                                    .merchantName(merchantName)
                                    .tranDate(transactionTime)
                                    .merchantAmount(transactionAmount)
                                    .merchantCurrency(transactionCurrency)
                                    .billingAmount(BigDecimal.ZERO)
                                    .billingCurrency("")
                                    .settleAmount(BigDecimal.ZERO)
                                    .settleCurrency("")
                                    .settleDate(null)
                                    .postDate(null)
                                    .transactionType("VERIFY")
                                    .status(TransactionUtil.statusHandle(transactionStatus))
                                    .code(errorMsgCode)
                                    .remark(errorMsg)
                                    .mcc(mcc)
                                    .fee(BigDecimal.ZERO) // LL does not provide fee information in AUTH
                                    .feeCurrency("")
                                    .feeInfo("")
                                    .merchantCountry(merchantCountryCode)
                                    .merchantCity("")
                                    .cardType(userCard.getCardType())
                                    .shareAccountId(userCard.getShareAccountId())
                                    .build();
                            transactionService.save(transaction);
                            riskQueue.add(transactionId);
                        }
                        default ->
                                throw new ProgramException("Unsupported transaction type: " + requestBody.getString("type"));
                    }
                } catch (Exception e) {
                    logger.error("Error processing LL message: {}", e.getMessage(), e);
                    throw new ProgramException("Error processing LL message: " + e.getMessage());
                } finally {
                    if (lock.isHeldByCurrentThread()) {
                        lock.unlock();
                    }
                }
            }
            default -> logger.warn("Unknown transaction type: {}", requestBody.getString("transactionType"));
        }

    }

    private void auth(
            String transactionId,
            String accountTransactionId,
            UserCard userCard,
            CardType cardType,
            ShareAccount shareAccount,
            UserCardWallet userCardWallet,
            ShareAccountConfig shareAccountConfig,
            ChannelBaseService channelBaseService,
            BigDecimal billingAmount,
            String status,
            String merchantCountry
    ) {
        logger.info("当前处理交易:{}, 用户卡ID:{}, 卡类型:{}, 交易金额:{}, 状态:{}, 商户国家:{}", transactionId, userCard.getId(), cardType.getCode(), billingAmount, status, merchantCountry);
        if (userCard.getCardType().equals(CardTypeEnum.NORMAL.getCode())) {
            logger.info("当前号段: {}, 卡号: {}, 授权手续费基数: {}, 比例: {}，小金额手续费基数: {}, 比例: {}, 阈值: {} , 跨境手续费基数: {}, 比例: {}, 失败手续费基数: {}, 比例: {}, ", cardType.getSectionNo(), userCard.getCardNumber(),
                    userCardWallet.getAuthFee(), userCardWallet.getAuthFeeRate(),
                    userCardWallet.getMinAuthFee(), userCardWallet.getMinAuthFeeRate(), cardType.getMinAuthThresholdAmount(),
                    userCardWallet.getCrossBorderFee(), userCardWallet.getCrossBorderFeeRate(),
                    userCardWallet.getFailFee(), userCardWallet.getFailFeeRate()
            );

        } else {
            logger.info("当前号段: {}, 卡号: {}, 授权手续费基数: {}, 比例: {}，小金额手续费基数: {}, 比例: {}, 阈值: {} , 跨境手续费基数: {}, 比例: {}, 失败手续费基数: {}, 比例: {}, ", cardType.getSectionNo(), userCard.getCardNumber(),
                    shareAccountConfig.getAuthFee(), shareAccountConfig.getAuthFeeRate(),
                    shareAccountConfig.getMinAuthFee(), shareAccountConfig.getMinAuthFeeRate(), cardType.getMinAuthThresholdAmount(),
                    shareAccountConfig.getCrossBorderFee(), shareAccountConfig.getCrossBorderFeeRate(),
                    shareAccountConfig.getFailFee(), shareAccountConfig.getFailFeeRate()
            );
        }
        Wallet wallet = walletService.getByUserIdAndCurrencyCode(userCard.getUserId(), userCard.getCurrencyCode());
        Channel channel = channelService.getByCode(cardType.getChannelName());
        if (status.equals("FAILED")) {
            fail(
                    accountTransactionId,
                    userCard,
                    cardType,
                    shareAccount,
                    userCardWallet,
                    shareAccountConfig,
                    channelBaseService,
                    billingAmount,
                    channel,
                    wallet
            );
        } else {
            //授权手续费
            auth(
                    accountTransactionId,
                    userCard,
                    cardType,
                    shareAccount,
                    userCardWallet,
                    shareAccountConfig,
                    channelBaseService,
                    billingAmount,
                    channel,
                    wallet
            );
            //小金额手续费
            minAuth(
                    accountTransactionId,
                    userCard,
                    cardType,
                    shareAccount,
                    userCardWallet,
                    shareAccountConfig,
                    channelBaseService,
                    billingAmount,
                    channel,
                    wallet
            );
            //跨境手续费
            crossBorder(
                    accountTransactionId,
                    userCard,
                    cardType,
                    shareAccount,
                    userCardWallet,
                    shareAccountConfig,
                    channelBaseService,
                    billingAmount,
                    channel,
                    wallet,
                    merchantCountry
            );
        }
    }

    private void fail(
            String accountTransactionId,
            UserCard userCard,
            CardType cardType,
            ShareAccount shareAccount,
            UserCardWallet userCardWallet,
            ShareAccountConfig shareAccountConfig,
            ChannelBaseService channelBaseService,
            BigDecimal billingAmount,
            Channel channel,
            Wallet wallet
    ) {
        //失败手续费
        BigDecimal failFee;
        if (channel.getHasShareAccount() && userCard.getCardType().equals(CardTypeEnum.SHARE.getCode())) {
            failFee = billingAmount
                    .multiply(shareAccountConfig.getFailFeeRate().divide(BigDecimal.valueOf(100), 4, RoundingMode.HALF_UP))
                    .add(shareAccountConfig.getFailFee()).setScale(2, RoundingMode.HALF_UP);
        } else {
            failFee = billingAmount
                    .multiply(userCardWallet.getFailFeeRate().divide(BigDecimal.valueOf(100), 4, RoundingMode.HALF_UP))
                    .add(userCardWallet.getFailFee()).setScale(2, RoundingMode.HALF_UP);
        }
        if (failFee.compareTo(BigDecimal.ZERO) <= 0) {
            logger.info("交易失败，失败手续费为0，不扣除失败手续费: {}", failFee);
            return;
        }
        if (channel.getHasShareAccount() && userCard.getCardType().equals(CardTypeEnum.SHARE.getCode())) {
            BigDecimal shareBalance = channelBaseService.queryAccountBalance(shareAccount.getAccountId());
            if (shareBalance.compareTo(failFee) >= 0) {
                logger.info("共享账户扣除共享账户的钱");
                TransferDto transferDto = new TransferDto();
                transferDto.setAccountId(shareAccount.getAccountId());
                transferDto.setAmount(failFee);
                transferDto.setType("transfer_out");
                Boolean transfer = channelBaseService.transfer(transferDto);
                if (transfer) {
                    shareAccountDetailService.create(
                            shareAccount.getId(),
                            channelBaseService.queryAccountBalance(shareAccount.getAccountId()),
                            "USD",
                            shareAccount.getChannelCode(),
                            "FAIL_AUTH_FEE",
                            accountTransactionId,
                            failFee,
                            "",
                            false,
                            cardType.getId(),
                            userCard.getId(),
                            shareAccount.getUserId(),
                            shareAccount.getUserId()
                    );
                    return;
                }
            }
        }
        if (failFee.compareTo(BigDecimal.ZERO) > 0) {
            logger.info("交易失败，扣除失败手续费: {}", failFee);
            BigDecimal balance = walletService.subtractUsableQuotaById(
                    wallet.getId(),
                    failFee,
                    2,
                    true
            );
            walletDetailService.failFee(
                    userCard.getId(),
                    cardType.getId(),
                    userCard.getChannelCode(),
                    userCard.getCardNumber(),
                    accountTransactionId,
                    "",
                    failFee,
                    balance,
                    wallet.getCurrencyId(),
                    userCard.getUserId()
            );
        }
    }

    private void auth(
            String accountTransactionId,
            UserCard userCard,
            CardType cardType,
            ShareAccount shareAccount,
            UserCardWallet userCardWallet,
            ShareAccountConfig shareAccountConfig,
            ChannelBaseService channelBaseService,
            BigDecimal billingAmount,
            Channel channel,
            Wallet wallet) {
        BigDecimal authFee;
        if (channel.getHasShareAccount() && userCard.getCardType().equals(CardTypeEnum.SHARE.getCode())) {
            authFee = billingAmount
                    .multiply(shareAccountConfig.getAuthFeeRate().divide(BigDecimal.valueOf(100), 4, RoundingMode.HALF_UP))
                    .add(shareAccountConfig.getAuthFee()).setScale(2, RoundingMode.HALF_UP);
        } else {
            authFee = billingAmount
                    .multiply(userCardWallet.getAuthFeeRate().divide(BigDecimal.valueOf(100), 4, RoundingMode.HALF_UP))
                    .add(userCardWallet.getAuthFee()).setScale(2, RoundingMode.HALF_UP);
        }
        if (authFee.compareTo(BigDecimal.ZERO) > 0) {
            logger.info("交易成功，扣除授权手续费: {}", authFee);
            if (channel.getHasShareAccount() && userCard.getCardType().equals(CardTypeEnum.SHARE.getCode())) {
                BigDecimal shareBalance = channelBaseService.queryAccountBalance(shareAccount.getAccountId());
                if (shareBalance.compareTo(authFee) >= 0) {
                    logger.info("共享账户扣除共享账户的钱");
                    TransferDto transferDto = new TransferDto();
                    transferDto.setAccountId(shareAccount.getAccountId());
                    transferDto.setAmount(authFee);
                    transferDto.setType("transfer_out");
                    Boolean transfer = channelBaseService.transfer(transferDto);
                    if (transfer) {
                        shareAccountDetailService.create(
                                shareAccount.getId(),
                                channelBaseService.queryAccountBalance(shareAccount.getAccountId()),
                                "USD",
                                shareAccount.getChannelCode(),
                                "AUTH_FEE",
                                accountTransactionId,
                                authFee,
                                "",
                                false,
                                cardType.getId(),
                                userCard.getId(),
                                shareAccount.getUserId(),
                                shareAccount.getUserId()
                        );
                        return;
                    }
                }
            }
            BigDecimal balance = walletService.subtractUsableQuotaById(
                    wallet.getId(),
                    authFee,
                    1,
                    true
            );
            walletDetailService.authFee(
                    userCard.getId(),
                    cardType.getId(),
                    userCard.getChannelCode(),
                    userCard.getCardNumber(),
                    accountTransactionId,
                    "",
                    authFee,
                    balance,
                    wallet.getCurrencyId(),
                    userCard.getUserId()
            );
        }
    }

    private void minAuth(
            String accountTransactionId,
            UserCard userCard,
            CardType cardType,
            ShareAccount shareAccount,
            UserCardWallet userCardWallet,
            ShareAccountConfig shareAccountConfig,
            ChannelBaseService channelBaseService,
            BigDecimal billingAmount,
            Channel channel,
            Wallet wallet
    ) {
        if (billingAmount.compareTo(cardType.getMinAuthThresholdAmount()) < 1 && billingAmount.compareTo(BigDecimal.ZERO) > 0) {

            BigDecimal minAuthFee;
            if (channel.getHasShareAccount() && userCard.getCardType().equals(CardTypeEnum.SHARE.getCode())) {
                minAuthFee = billingAmount
                        .multiply(shareAccountConfig.getMinAuthFeeRate().divide(BigDecimal.valueOf(100), 4, RoundingMode.HALF_UP))
                        .add(shareAccountConfig.getMinAuthFee()).setScale(2, RoundingMode.HALF_UP);
            } else {
                minAuthFee = billingAmount
                        .multiply(userCardWallet.getMinAuthFeeRate().divide(BigDecimal.valueOf(100), 4, RoundingMode.HALF_UP))
                        .add(userCardWallet.getMinAuthFee()).setScale(2, RoundingMode.HALF_UP);
            }
            if (minAuthFee.compareTo(BigDecimal.ZERO) > 0) {
                logger.info("交易成功，扣除小金额手续费: {}", minAuthFee);
                if (channel.getHasShareAccount() && userCard.getCardType().equals(CardTypeEnum.SHARE.getCode())) {
                    BigDecimal shareBalance = channelBaseService.queryAccountBalance(shareAccount.getAccountId());
                    if (shareBalance.compareTo(minAuthFee) >= 0) {
                        logger.info("共享账户扣除共享账户的钱");
                        TransferDto transferDto = new TransferDto();
                        transferDto.setAccountId(shareAccount.getAccountId());
                        transferDto.setAmount(minAuthFee);
                        transferDto.setType("transfer_out");
                        Boolean transfer = channelBaseService.transfer(transferDto);
                        if (transfer) {
                            shareAccountDetailService.create(
                                    shareAccount.getId(),
                                    channelBaseService.queryAccountBalance(shareAccount.getAccountId()),
                                    "USD",
                                    shareAccount.getChannelCode(),
                                    "MIN_AUTH_FEE",
                                    accountTransactionId,
                                    minAuthFee,
                                    "",
                                    false,
                                    cardType.getId(),
                                    userCard.getId(),
                                    shareAccount.getUserId(),
                                    shareAccount.getUserId()
                            );
                            return;
                        }
                    }
                }
                BigDecimal balance = walletService.subtractUsableQuotaById(
                        wallet.getId(),
                        minAuthFee,
                        1,
                        true
                );
                walletDetailService.authMinFee(
                        userCard.getId(),
                        cardType.getId(),
                        userCard.getChannelCode(),
                        userCard.getCardNumber(),
                        accountTransactionId,
                        "",
                        minAuthFee,
                        balance,
                        wallet.getCurrencyId(),
                        userCard.getUserId()
                );
            }

        }
    }

    private void crossBorder(
            String accountTransactionId,
            UserCard userCard,
            CardType cardType,
            ShareAccount shareAccount,
            UserCardWallet userCardWallet,
            ShareAccountConfig shareAccountConfig,
            ChannelBaseService channelBaseService,
            BigDecimal billingAmount,
            Channel channel,
            Wallet wallet,
            String merchantCountry
    ) {
        if (!merchantCountry.startsWith(cardType.getCountry())) {
            BigDecimal crossBorderFee;
            if (channel.getHasShareAccount() && userCard.getCardType().equals(CardTypeEnum.SHARE.getCode())) {
                crossBorderFee = billingAmount
                        .multiply(shareAccountConfig.getCrossBorderFeeRate().divide(BigDecimal.valueOf(100), 4, RoundingMode.HALF_UP))
                        .add(shareAccountConfig.getCrossBorderFee()).setScale(2, RoundingMode.HALF_UP);
            } else {
                crossBorderFee = billingAmount
                        .multiply(userCardWallet.getCrossBorderFeeRate().divide(BigDecimal.valueOf(100), 4, RoundingMode.HALF_UP))
                        .add(userCardWallet.getCrossBorderFee()).setScale(2, RoundingMode.HALF_UP);
            }
            if (crossBorderFee.compareTo(BigDecimal.ZERO) > 0) {
                logger.info("交易成功，扣除跨境手续费: {}", crossBorderFee);
                if (channel.getHasShareAccount() && userCard.getCardType().equals(CardTypeEnum.SHARE.getCode())) {
                    BigDecimal shareBalance = channelBaseService.queryAccountBalance(shareAccount.getAccountId());
                    if (shareBalance.compareTo(crossBorderFee) >= 0) {
                        logger.info("共享账户扣除共享账户的钱");
                        TransferDto transferDto = new TransferDto();
                        transferDto.setAccountId(shareAccount.getAccountId());
                        transferDto.setAmount(crossBorderFee);
                        transferDto.setType("transfer_out");
                        Boolean transfer = channelBaseService.transfer(transferDto);
                        if (transfer) {
                            shareAccountDetailService.create(
                                    shareAccount.getId(),
                                    channelBaseService.queryAccountBalance(shareAccount.getAccountId()),
                                    "USD",
                                    shareAccount.getChannelCode(),
                                    "CROSS_BORDER_AUTH_FEE",
                                    accountTransactionId,
                                    crossBorderFee,
                                    "",
                                    false,
                                    cardType.getId(),
                                    userCard.getId(),
                                    shareAccount.getUserId(),
                                    shareAccount.getUserId()
                            );
                            return;
                        }
                    }
                }
                BigDecimal balance = walletService.subtractUsableQuotaById(
                        wallet.getId(),
                        crossBorderFee,
                        1,
                        true
                );
                walletDetailService.crossBorderFee(
                        userCard.getId(),
                        cardType.getId(),
                        userCard.getChannelCode(),
                        userCard.getCardNumber(),
                        accountTransactionId,
                        "",
                        crossBorderFee,
                        balance,
                        wallet.getCurrencyId(),
                        userCard.getUserId()
                );
            }
        }
    }


    private void reversal(
            String transactionId,
            String accountTransactionId,
            UserCard userCard,
            CardType cardType,
            ShareAccount shareAccount,
            ShareAccountConfig shareAccountConfig,
            ChannelBaseService channelBaseService,
            UserCardWallet userCardWallet,
            BigDecimal billingAmount
    ) {
        logger.info("当前处理交易:{}, 用户卡ID:{}, 卡类型:{}, 交易金额:{}", transactionId, userCard.getId(), cardType.getCode(), billingAmount);
        if (userCard.getCardType().equals(CardTypeEnum.SHARE.getCode())) {
            logger.info("当前号段: {}, 卡号: {}, 撤销手续费基数: {}, 比例: {}, ", cardType.getSectionNo(), userCard.getCardNumber(),
                    shareAccountConfig.getReversalFee(), shareAccountConfig.getReversalFeeRate()
            );
        } else {
            logger.info("当前号段: {}, 卡号: {}, 撤销手续费基数: {}, 比例: {}, ", cardType.getSectionNo(), userCard.getCardNumber(),
                    userCardWallet.getReversalFee(), userCardWallet.getReversalFeeRate()
            );

        }

        Wallet wallet = walletService.getByUserIdAndCurrencyCode(userCard.getUserId(), userCard.getCurrencyCode());
        Channel channel = channelService.getByCode(cardType.getChannelName());

        //撤销手续费
        BigDecimal reversalFee;
        if (channel.getHasShareAccount() && userCard.getCardType().equals(CardTypeEnum.SHARE.getCode())) {
            reversalFee = billingAmount
                    .multiply(shareAccountConfig.getReversalFeeRate().divide(BigDecimal.valueOf(100), 4, RoundingMode.HALF_UP))
                    .add(shareAccountConfig.getReversalFee()).setScale(2, RoundingMode.HALF_UP);
        } else {
            reversalFee = billingAmount
                    .multiply(userCardWallet.getReversalFeeRate().divide(BigDecimal.valueOf(100), 4, RoundingMode.HALF_UP))
                    .add(userCardWallet.getReversalFee()).setScale(2, RoundingMode.HALF_UP);
        }

        if (reversalFee.compareTo(BigDecimal.ZERO) > 0) {
            logger.info("交易成功，扣除撤销手续费: {}", reversalFee);
            if (channel.getHasShareAccount() && userCard.getCardType().equals(CardTypeEnum.SHARE.getCode())) {
                BigDecimal shareBalance = channelBaseService.queryAccountBalance(shareAccount.getAccountId());
                if (shareBalance.compareTo(reversalFee) >= 0) {
                    logger.info("共享账户扣除共享账户的钱");
                    TransferDto transferDto = new TransferDto();
                    transferDto.setAccountId(shareAccount.getAccountId());
                    transferDto.setAmount(reversalFee);
                    transferDto.setType("transfer_out");
                    Boolean transfer = channelBaseService.transfer(transferDto);
                    if (transfer) {
                        shareAccountDetailService.create(
                                shareAccount.getId(),
                                channelBaseService.queryAccountBalance(shareAccount.getAccountId()),
                                "USD",
                                shareAccount.getChannelCode(),
                                "REVERSAL_FEE",
                                accountTransactionId,
                                reversalFee,
                                "",
                                false,
                                cardType.getId(),
                                userCard.getId(),
                                shareAccount.getUserId(),
                                shareAccount.getUserId()
                        );
                        return;
                    }
                }
            }
            BigDecimal balance = walletService.subtractUsableQuotaById(
                    wallet.getId(),
                    reversalFee,
                    1,
                    true
            );
            walletDetailService.reversalFee(
                    userCard.getId(),
                    cardType.getId(),
                    userCard.getChannelCode(),
                    userCard.getCardNumber(),
                    accountTransactionId,
                    "",
                    reversalFee,
                    balance,
                    wallet.getCurrencyId(),
                    userCard.getUserId()
            );
        }

    }

    private void refund(
            String transactionId,
            String accountTransactionId,
            UserCard userCard,
            CardType cardType,
            ShareAccount shareAccount,
            ShareAccountConfig shareAccountConfig,
            ChannelBaseService channelBaseService,
            UserCardWallet userCardWallet,
            BigDecimal billingAmount
    ) {
        logger.info("当前处理交易:{}, 用户卡ID:{}, 卡类型:{}, 交易金额:{}", transactionId, userCard.getId(), cardType.getCode(), billingAmount);
        if (userCard.getCardType().equals(CardTypeEnum.SHARE.getCode())) {
            logger.info("当前号段: {}, 卡号: {}, 退款手续费基数: {}, 比例: {}, ", cardType.getSectionNo(), userCard.getCardNumber(),
                    shareAccountConfig.getRefundFee(), shareAccountConfig.getRefundFeeRate()
            );
        } else {
            logger.info("当前号段: {}, 卡号: {}, 退款手续费基数: {}, 比例: {}, ", cardType.getSectionNo(), userCard.getCardNumber(),
                    userCardWallet.getRefundFee(), userCardWallet.getRefundFeeRate()
            );
        }
        Wallet wallet = walletService.getByUserIdAndCurrencyCode(userCard.getUserId(), userCard.getCurrencyCode());
        Channel channel = channelService.getByCode(cardType.getChannelName());
        //退款手续费
        BigDecimal refundFee;
        if (channel.getHasShareAccount() && userCard.getCardType().equals(CardTypeEnum.SHARE.getCode())) {
            refundFee = billingAmount
                    .multiply(shareAccountConfig.getRefundFeeRate().divide(BigDecimal.valueOf(100), 4, RoundingMode.HALF_UP))
                    .add(shareAccountConfig.getRefundFee()).setScale(2, RoundingMode.HALF_UP);
        } else {
            refundFee = billingAmount
                    .multiply(userCardWallet.getRefundFeeRate().divide(BigDecimal.valueOf(100), 4, RoundingMode.HALF_UP))
                    .add(userCardWallet.getRefundFee()).setScale(2, RoundingMode.HALF_UP);
        }

        if (refundFee.compareTo(BigDecimal.ZERO) > 0) {
            logger.info("交易成功，扣除退款手续费: {}", refundFee);
            if (channel.getHasShareAccount() && userCard.getCardType().equals(CardTypeEnum.SHARE.getCode())) {
                BigDecimal shareBalance = channelBaseService.queryAccountBalance(shareAccount.getAccountId());
                if (shareBalance.compareTo(refundFee) >= 0) {
                    logger.info("共享账户扣除共享账户的钱");
                    TransferDto transferDto = new TransferDto();
                    transferDto.setAccountId(shareAccount.getAccountId());
                    transferDto.setAmount(refundFee);
                    transferDto.setType("transfer_out");
                    Boolean transfer = channelBaseService.transfer(transferDto);
                    if (transfer) {
                        shareAccountDetailService.create(
                                shareAccount.getId(),
                                channelBaseService.queryAccountBalance(shareAccount.getAccountId()),
                                "USD",
                                shareAccount.getChannelCode(),
                                "REFUND_FEE",
                                accountTransactionId,
                                refundFee,
                                "",
                                false,
                                cardType.getId(),
                                userCard.getId(),
                                shareAccount.getUserId(),
                                shareAccount.getUserId()
                        );
                        return;
                    }
                }
            }
            BigDecimal balance = walletService.subtractUsableQuotaById(
                    wallet.getId(),
                    refundFee,
                    1,
                    true
            );
            walletDetailService.refundFee(
                    userCard.getId(),
                    cardType.getId(),
                    userCard.getChannelCode(),
                    userCard.getCardNumber(),
                    accountTransactionId,
                    "",
                    refundFee,
                    balance,
                    wallet.getCurrencyId(),
                    userCard.getUserId()
            );
        }

    }

    @PostMapping("emailNotification")
    public void emailNotification(
            @RequestBody String payload
    ) {
        logger.info("Received email notification: {}", payload);
        JSONObject requestBody;
        try {
            requestBody = JSONUtil.toBean(payload, JSONObject.class);
        } catch (Exception e) {
            logger.error("Error parsing request body: {}", e.getMessage(), e);
            return;
        }
        //邮件来源地址
        String smtpFrom = requestBody.getString("smtp_from");
        //邮件接收地址
        String smtpTo = requestBody.getString("smtp_to");
        Cardholder cardholder = cardholderService.getByEmail(smtpTo);
        if (smtpFrom.contains("photonpay")) {
            String plainText = requestBody.getString("plain_text");
            String cardNumber = "";
            String verificationCode = "";
            String merchantName = "";
            String amount = "";
            String currency = "";
            String lastFourCardNumber = "";
            if (StringUtils.isBlank(plainText)) {
                Map<String, String> htmlContent = extractEmailDataRobust(requestBody.getString("body"));
                cardNumber = htmlContent.getOrDefault("cardNumber", "");
                verificationCode = htmlContent.getOrDefault("verifyCode", "");
                merchantName = "";
                amount = htmlContent.getOrDefault("amount", "");
                currency = htmlContent.getOrDefault("currency", "");
            } else {
                String replace = plainText.replaceAll("\r\n", "");
                cardNumber = replace.replaceAll(".*card number (\\d{6}\\*{6}\\d{4}).*", "$1");
                verificationCode = replace.replaceAll(".*verification code: (\\d{6}).*", "$1");
                merchantName = "";
                amount = replace.replaceAll(".*transaction for (\\d+\\.\\d{2}).*", "$1");
                currency = replace.replaceAll(".*transaction for \\d+\\.\\d{2} (\\w+).*", "$1");
                //取后四位
            }
            lastFourCardNumber = cardNumber.replaceAll(".*(\\d{4})$", "$1");

            List<UserCard> userCards = userCardService.listByCardholderIdAndLastFourCardNumber(
                    cardholder.getId(),
                    lastFourCardNumber
            );
            if (userCards.isEmpty()) {
                logger.warn("No user card found for cardholder ID: {} and last four digits: {}", cardholder.getId(), lastFourCardNumber);
                return;
            }
            for (UserCard userCard : userCards) {
                Otp otp = Otp.builder()
                        .userId(userCard == null ? null : userCard.getUserId())
                        .code(verificationCode)
                        .type("TRANSACTION")
                        .userCardId(userCard != null ? userCard.getId() : null)
                        .cardTypeId(userCard != null ? userCard.getCardTypeId() : null)
                        .cardholderId(cardholder.getId())
                        .currency(currency)
                        .amount(new BigDecimal(amount))
                        .cardNumber(userCard != null ? userCard.getCardNumber() : cardNumber)
                        .lastFourCardNumber(lastFourCardNumber)
                        .merchantName(merchantName)
                        .sourceInfo(payload)
                        .build();
                otpService.save(otp);
            }
        }
    }


    public static Map<String, String> extractEmailDataRobust(String htmlContent) {
        Map<String, String> result = new HashMap<>();

        // 移除换行符和多余空格，简化匹配
        String cleanedContent = htmlContent.replaceAll("\\s+", " ").replaceAll(">\\s+<", "><");

        try {
            // 提取卡号
            Pattern cardPattern = Pattern.compile("card number[^<]*<span[^>]*>([^<]+)</span>", Pattern.CASE_INSENSITIVE);
            Matcher cardMatcher = cardPattern.matcher(cleanedContent);
            if (cardMatcher.find()) {
                result.put("cardNumber", cardMatcher.group(1).trim());
            }

            // 提取验证码（多种可能的关键词）
            Pattern codePattern = Pattern.compile("(verification code|passcode|code)[^<]*<span[^>]*>([^<]+)</span>", Pattern.CASE_INSENSITIVE);
            Matcher codeMatcher = codePattern.matcher(cleanedContent);
            if (codeMatcher.find()) {
                result.put("verifyCode", codeMatcher.group(2).trim());
            }

            // 提取金额和币种
            Pattern amountPattern = Pattern.compile("transaction for[^<]*<span[^>]*>([\\d,]+\\.[\\d]{2})\\s+([A-Z]{3})</span>", Pattern.CASE_INSENSITIVE);
            Matcher amountMatcher = amountPattern.matcher(cleanedContent);
            if (amountMatcher.find()) {
                result.put("amount", amountMatcher.group(1).trim());
                result.put("currency", amountMatcher.group(2).trim());
            }

            // 提取商户名
            Pattern merchantPattern = Pattern.compile("Best regards,[^<]*<p[^>]*>([^<]+)</p>", Pattern.CASE_INSENSITIVE);
            Matcher merchantMatcher = merchantPattern.matcher(cleanedContent);
            if (merchantMatcher.find()) {
                result.put("merchantName", merchantMatcher.group(1).trim());
            }

        } catch (Exception e) {
            System.err.println("提取数据时发生错误: " + e.getMessage());
        }

        return result;
    }

}

